//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
///
//==================================================================

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gl/glew.h>
#include "psys.h"
#include "pwindow.h"
#include "gfxutils.h"
#include "gadget.h"

//==================================================================
using namespace	PSYSGFX;

//==================================================================
//== GGET_Manager
//==================================================================
GGET_EditBox	*GGET_Manager::AddEditBox( int gget_id, int x, int y, int w, int h, const TCHAR *txtp )
{
	GGET_EditBox	*ggetp = new GGET_EditBox( this );
	ggetp->_id = gget_id;
	ggetp->SetRect( x, y, w, h );
	ggetp->SetText( txtp );

	_items.push_back( (GGET_Item *)ggetp );
	//_items.append_or_delete( (GGET_Item *)ggetp );

	return ggetp;
}

//==================================================================
GGET_Button		*GGET_Manager::AddButton( int gget_id, int x, int y, int w, int h, const TCHAR *titlep )
{
	GGET_Button	*ggetp = new GGET_Button( this );
	ggetp->_id = gget_id;
	ggetp->SetRect( x, y, w, h );
	ggetp->SetText( titlep );

	_items.push_back( (GGET_Item *)ggetp );
	//_items.append_or_delete( (GGET_Item *)ggetp );

	return ggetp;
}

//==================================================================
GGET_Tab		*GGET_Manager::AddTab( int gget_id, int x, int y, int w, int h, const TCHAR *titlep )
{
	GGET_Tab	*ggetp = new GGET_Tab( this );
	ggetp->_id = gget_id;
	ggetp->SetRect( x, y, w, h );
	ggetp->SetText( titlep );
	_items.push_back( (GGET_Item *)ggetp );
	//_items.append_or_delete( (GGET_Item *)ggetp );

	ggetp->_close_buttp = new GGET_Button( this );
	ggetp->_close_buttp->_id = gget_id | 65536;
	ggetp->_close_buttp->_flags |= GGET_FLG_CLOSE_TAB_BUTT;
	ggetp->_close_buttp->SetText( titlep );
	_items.push_back( (GGET_Item *)ggetp->_close_buttp );
	//_items.append_or_delete( (GGET_Item *)ggetp->_close_buttp );

	ggetp->CalcButtFrame();

	return ggetp;
}

//==================================================================
GGET_StaticText *GGET_Manager::AddStaticText( int gget_id, int x, int y, int w, int h, const TCHAR *txtp )
{
	GGET_StaticText	*ggetp = new GGET_StaticText( this );
	ggetp->_id = gget_id;
	ggetp->SetRect( x, y, w, h );
	ggetp->SetText( txtp );

	_items.push_back( (GGET_Item *)ggetp );
	//_items.append_or_delete( (GGET_Item *)ggetp );

	return ggetp;
}

//==================================================================
void GGET_Manager::RemoveItem( int gget_id )
{
	if ( GGET_Item	*itemp = FindGadget( gget_id ) )
	{
		if NOT( itemp->_flags & GGET_FLG_REMOVED )
		{
			itemp->_flags |= GGET_FLG_REMOVED;
			_base_manager.InvalidateDisplay();
		}
	}

	if ( GGET_Item	*itemp = FindGadget( gget_id | 65536 ) )
	{
		if NOT( itemp->_flags & GGET_FLG_REMOVED )
		{
			itemp->_flags |= GGET_FLG_REMOVED;
			_base_manager.InvalidateDisplay();
		}
	}
}

//==================================================================
void GGET_Manager::setGadgetFlags( int gget_id, u_int mask, bool onoff )
{
	if ( GGET_Item	*itemp = FindGadget( gget_id ) )
	{
		u_int	old_flags = itemp->_flags;

		if ( onoff )
			itemp->_flags |= mask;
		else
			itemp->_flags &= ~mask;

		if ( itemp->_flags != old_flags )
			_base_manager.InvalidateDisplay();
	}
}

//==================================================================
bool GGET_Manager::getGadgetFlags( int gget_id, u_int mask )
{
	if ( GGET_Item	*itemp = FindGadget( gget_id ) )
	{
		return (itemp->_flags & mask) == mask;
	}

	return false;
}

//==================================================================
void GGET_Manager::EnableGadget( int gget_id, bool onoff )
{
	setGadgetFlags( gget_id, GGET_FLG_DISABLED, !onoff );
}

//==================================================================
void GGET_Manager::SetToggled( int gget_id, bool onoff )
{
	setGadgetFlags( gget_id, GGET_FLG_BUTTON_TOGGLED, onoff );
}

//==================================================================
bool GGET_Manager::GetToggled( int gget_id )
{
	return getGadgetFlags( gget_id, GGET_FLG_BUTTON_TOGGLED );
}

//==================================================================
void GGET_Manager::SetGadgetText( int gget_id, const TCHAR *txtp )
{
	if ( GGET_Item	*itemp = FindGadget( gget_id ) )
		itemp->SetText( txtp );
}

//==================================================================
void GGET_Manager::OnPaint( WindowEvent *eventp )
{
	if NOT( _items.size() )
		return;

	glPushMatrix();	

	for (int i=0; i < _items.size(); ++i)
	{
		GGET_Item	*ggetp = _items[i];
		if ( ggetp->_flags & GGET_FLG_REMOVED )
			continue;

		if NOT( ggetp->_flags & GGET_FLG_INVISIBLE )
		{
			glLoadIdentity();
			glTranslatef( ggetp->_rect._x, ggetp->_rect._y, 0 );
			ggetp->Paint();
		}
	}

	glPopMatrix();
}

//==================================================================
int GGET_Manager::EventFilter_s( void *userdatap, WindowEvent *eventp )
{
	return ((GGET_Manager *)userdatap)->EventFilter( eventp );
}

//==================================================================
static int in_rect( int x, int y, const PSYS::RectF &rect )
{
	return (x >= rect._x && y >= rect._y && x < rect._x+rect._w && y < rect._y+rect._h);
}

//==================================================================
int GGET_Manager::EventFilter( WindowEvent *eventp )
{
	GGET_Item	*ggetp;
	GGET_Item	*found_clicked_editp;
	int			retval = 0;

	switch ( eventp->GetType() )
	{
	case WindowEvent::ETYPE_KEYDOWN:
		for (int i=_items.size()-1; i >= 0; --i)
		{
			ggetp = _items[i];
			if ( ggetp->_flags & GGET_FLG_REMOVED )
				continue;

			if NOT( ggetp->_flags & (GGET_FLG_DISABLED|GGET_FLG_INVISIBLE) )
			{
				if ( ggetp->_flags & GGET_FLG_ACTIVE )
					if ( ggetp->_type == GGET_TYPE_EDIT )
					{
						if ( ((GGET_EditBox *)ggetp)->OnKeydown( eventp ) )
							_base_manager.InvalidateDisplay();
						// should return 1 here !
					}
			}
		}
		break;

	case WindowEvent::ETYPE_KEYCHAR:
		for (int i=_items.size()-1; i >= 0; --i)
		{
			ggetp = _items[i];
			if ( ggetp->_flags & GGET_FLG_REMOVED )
				continue;

			if NOT( ggetp->_flags & (GGET_FLG_DISABLED|GGET_FLG_INVISIBLE) )
				if ( ggetp->_flags & GGET_FLG_ACTIVE )
					if ( ggetp->_type == GGET_TYPE_EDIT )
					{
						if ( ((GGET_EditBox *)ggetp)->OnKeychar( _cback_userdatap, eventp, _cback ) )
							_base_manager.InvalidateDisplay();
						// should return 1 here !
					}
		}
		break;

	case WindowEvent::ETYPE_MOUSEMOVE:
		for (int i=_items.size()-1; i >= 0; --i)
		{
			ggetp = _items[i];
			if ( ggetp->_flags & GGET_FLG_REMOVED )
				continue;

			if NOT( ggetp->_flags & (GGET_FLG_DISABLED|GGET_FLG_INVISIBLE) )
			{
				switch( ggetp->_type )
				{
				case GGET_TYPE_BUTTON:
				case GGET_TYPE_TAB:
				case GGET_TYPE_CHECK:
					{
						u_int	old_flags = ggetp->_flags;

						if ( retval == 0 && in_rect( eventp->mouse_x, eventp->mouse_y, ggetp->_rect ) )
						{
							retval = 1;
							ggetp->_flags |= GGET_FLG_MOUSEOVER;
						}
						else
							ggetp->_flags &= ~(GGET_FLG_MOUSEOVER|GGET_FLG_PRESSED);

						if ( ggetp->_flags != old_flags )
							_base_manager.InvalidateDisplay();
					}
					break;
				}
			}
		}
		break;

	case WindowEvent::ETYPE_MOUSEMOVEDOUT:
		for (int i=_items.size()-1; i >= 0; --i)
		{
			ggetp = _items[i];
			if ( ggetp->_flags & GGET_FLG_REMOVED )
				continue;

			if NOT( ggetp->_flags & (GGET_FLG_DISABLED|GGET_FLG_INVISIBLE) )
			{
				switch( ggetp->_type )
				{
				case GGET_TYPE_BUTTON:
				case GGET_TYPE_TAB:
				case GGET_TYPE_CHECK:
					{
						u_int	old_flags = ggetp->_flags;

						ggetp->_flags &= ~(GGET_FLG_MOUSEOVER|GGET_FLG_PRESSED);

						if ( ggetp->_flags != old_flags )
							_base_manager.InvalidateDisplay();
					}
					break;

				}
			}
		}
		break;

	case WindowEvent::ETYPE_LBUTTONDOWN:
		found_clicked_editp = NULL;
		for (int i=_items.size()-1; i >= 0; --i)
		{
			ggetp = _items[i];
			if ( ggetp->_flags & GGET_FLG_REMOVED )
				continue;

			if NOT( ggetp->_flags & (GGET_FLG_DISABLED|GGET_FLG_INVISIBLE) )
			{
				switch( ggetp->_type )
				{
				case GGET_TYPE_BUTTON:
				case GGET_TYPE_TAB:
				case GGET_TYPE_CHECK:
					{
					u_int	old_flags = ggetp->_flags;

					if ( retval == 0 && in_rect( eventp->mouse_x, eventp->mouse_y, ggetp->_rect ) )
					{
						retval = 1;
						ggetp->_flags |= GGET_FLG_PRESSED;
					}
					else
						ggetp->_flags &= ~GGET_FLG_PRESSED;

					if ( ggetp->_flags != old_flags )
						_base_manager.InvalidateDisplay();
					}
					break;

				case GGET_TYPE_EDIT:
					if ( retval == 0 && in_rect( eventp->mouse_x, eventp->mouse_y, ggetp->_rect ) )
					{
						retval = 1;
						found_clicked_editp = ggetp;
					}
					break;
				}
			}
		}
		// if one editbox was clicked in, then proceed to activate it, exclusively !
		if ( found_clicked_editp )
		{
			ActivateOne( found_clicked_editp );
			_base_manager.InvalidateDisplay();
		}
		break;

	case WindowEvent::ETYPE_LBUTTONUP:
		for (int i=_items.size()-1; i >= 0; --i)
		{
			ggetp = _items[i];
			if ( ggetp->_flags & GGET_FLG_REMOVED )
				continue;

			if NOT( ggetp->_flags & (GGET_FLG_DISABLED|GGET_FLG_INVISIBLE) )
			{
				switch( ggetp->_type )
				{
				case GGET_TYPE_BUTTON:
				case GGET_TYPE_TAB:
				case GGET_TYPE_CHECK:
					if ( retval == 0 && in_rect( eventp->mouse_x, eventp->mouse_y, ggetp->_rect ) )
					{
						u_int	old_flags = ggetp->_flags;

						retval = 1;
						if ( _cback )
						{
							if ( ggetp->_id & 65536 )
								_cback( _cback_userdatap, ggetp->_id & ~65536, ggetp, GGET_CB_ACTION_CLOSETAB );
							else
								_cback( _cback_userdatap, ggetp->_id, ggetp, GGET_CB_ACTION_CLICK );
						}

						ggetp->_flags &= ~GGET_FLG_PRESSED;

						if ( ggetp->_flags != old_flags )
							_base_manager.InvalidateDisplay();
					}
					break;
				}
			}
		}
		break;

	case WindowEvent::ETYPE_PAINT:
		begin_2D( NULL, NULL );
			OnPaint( eventp );
		end_2D();
		break;
	}

	return retval;
}

//==================================================================
void GGET_Manager::ActivateOne( GGET_Item *activatep )
{
	for (int i=0; i < _items.size(); ++i)
	{
		GGET_Item	*ggetp = _items[i];
		if ( ggetp->_flags & GGET_FLG_REMOVED )
			continue;

		if ( ggetp == activatep )
			ggetp->_flags |= GGET_FLG_ACTIVE;
		else
			ggetp->_flags &= ~GGET_FLG_ACTIVE;
	}
}

//==================================================================
GGET_Item	*GGET_Manager::FindGadget( int gget_id )
{
	for (int i=0; i < _items.size(); ++i)
	{
		if ( _items[i]->_flags & GGET_FLG_REMOVED )
			continue;

		if ( _items[i]->_id == gget_id )
			return _items[i];
	}

	return 0;
}

//==================================================================
void GGET_Manager::ChangeGadgetID( int old_gget_id, int new_gget_id )
{
	for (int i=0; i < _items.size(); ++i)
	{
		if ( _items[i]->_flags & GGET_FLG_REMOVED )
			continue;

		if ( (_items[i]->_id & ~65536) == old_gget_id )
		{
			u_int	closebut_id_flg = _items[i]->_id & 65536;

			_items[i]->_id = new_gget_id | closebut_id_flg;
		}
	}
}